package hydrap2p.hubmanager;

import java.util.LinkedList;
import java.util.List;

import hydrap2p.hub.HubStatus;

public class ListenerQueue implements Listener {

	private boolean running;
	private final Listeners listeners;
	private final Listener listener;
	private final List<ListenerEvent> queue;
	private final Thread pusher;
	
	public ListenerQueue(Listeners listeners, Listener listener)
	{
		queue=new LinkedList<ListenerEvent>();
		this.listeners=listeners;
		this.listener=listener;
		running=true;
		
		pusher=new Thread(new QueuePusher(), "QueuePusher - "+listener.toString());
		pusher.start();
	}
	
	private interface ListenerEvent {
		public void doIt();
	}
	
	private void addSync(ListenerEvent event)
	{
		synchronized(queue)
		{
			queue.add(event);
			queue.notifyAll();
		}
	}

	@Override
	public void stop() {
		running=false;
		pusher.interrupt();
	}
	
	private class QueuePusher implements Runnable
	{
		@Override
		public void run() {
			while (running)
			{
				synchronized(queue)
				{
					if (queue.size()>0)
					{						
						queue.remove(0).doIt();
					}
					else
					{
						try {
							queue.wait();
						} catch (InterruptedException e) {
							// Signalled to update status or something
						}
					}
				}
				try {
					Thread.sleep(10);	// let other thread finish doing stuff
				} catch (InterruptedException e) {
					
				}	
			}
		}
	}
	
	
	@Override
	public void connectionAdded(final HubStatus status) {
		addSync(new ListenerEvent(){
			public void doIt(){
				try {
					listener.connectionAdded(status);
				} catch (Exception e) {
					died();
				}
			}
			@Override
			public String toString()
			{
				return "ConnectionAdded "+status.toString()+" for listener "+listener.toString();
			}
		});
	}

	@Override
	public void connectionChanged(final HubStatus status) {
		addSync(new ListenerEvent(){
			public void doIt(){
				try {
					listener.connectionChanged(status);
				} catch (Exception e) {
					died();
				}
			}
			@Override
			public String toString()
			{
				return "ConnectionChanged "+status.toString()+" for listener "+listener.toString();
			}
		});
	}

	@Override
	public void connectionRemoved(final HubStatus status) {
		addSync(new ListenerEvent(){
			public void doIt(){
				try {
					listener.connectionRemoved(status);
				} catch (Exception e) {
					died();
				}
			}
			@Override
			public String toString()
			{
				return "ConnectionRemoved "+status.toString()+" for listener "+listener.toString();
			}
		});
	}

	@Override
	public void connectionConnected(final HubStatus status) {
		addSync(new ListenerEvent(){
			public void doIt(){
				try {
					listener.connectionConnected(status);
				} catch (Exception e) {
					died();
				}
			}
			@Override
			public String toString()
			{
				return "ConnectedConnected "+status.toString()+" for listener "+listener.toString();
			}
		});
	}

	@Override
	public void protocolRegistered(final String name) {
		addSync(new ListenerEvent(){
			public void doIt(){
				try {
					listener.protocolRegistered(name);
				} catch (Exception e) {
					died();
				}
			}
			@Override
			public String toString()
			{
				return "ProtocolRegistered "+name+" for listener "+listener.toString();
			}
		});
	}

	@Override
	public void protocolUnregistered(final String name) {
		addSync(new ListenerEvent(){
			public void doIt(){
				try {
					listener.protocolUnregistered(name);
				} catch (Exception e) {
					died();
				}
			}
			@Override
			public String toString()
			{
				return "ProtocolUnregistered "+name+" for listener "+listener.toString();
			}
		});
	}

	private void died(){
		// this listener somehow died
		listeners.delListener(this);
	}
	
	@Override
	public int hashCode()
	{
		return listener.hashCode();
	}
	
	@Override
	public boolean equals(Object other)
	{
		return listener.equals(other);
	}
}
